#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cstring>
#include<string>
#include"log.hpp"

class Sock
{
private:
    const static int gbacklog = 10;
public:
    Sock(){}

    ~Sock(){}


    static int Socket()
    {
        //  创建套接字
        int sock = socket(AF_INET,SOCK_STREAM,0);
        if(sock < 0)
        {
            logMessage(ERROR,"Socket(): create socket fail");
            exit(2);
        }
        // 防止主动断开连接后无法立即bind
        int opt = 1;
        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR | SO_REUSEPORT,&opt,sizeof(opt));
        return sock;
    }

    static void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
    {
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family  = AF_INET;
        local.sin_addr.s_addr = inet_addr(ip.c_str());
        local.sin_port = htons(port);

        if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
        {
            logMessage(ERROR,"Bind(): bind fail");
            exit(3);
        }
    }

    static void Listen(int sock)
    {
        if(listen(sock,gbacklog) < 0)
        {
            logMessage(ERROR,"Listen(): listen fail");
            exit(4);
        }
    }

    static int Accept(int sock, std::string * ip, uint16_t* port)
    {
        struct sockaddr_in src;
        bzero(&src,sizeof src);
        socklen_t len = sizeof src;

        int serversock = accept(sock,(struct sockaddr*)&src,&len);
        if(serversock < 0)
        {
            logMessage(ERROR,"Accept(): accept fail");
            exit(5);
        }
        if(ip) *ip = inet_ntoa(src.sin_addr);
        if(port) *port=ntohs(src.sin_port);
        return serversock;
    }


    static bool Connect(int sock, const uint16_t &port, const std::string& ip)
    {
        struct sockaddr_in server;
        bzero(&server,sizeof server);
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        inet_aton(ip.c_str(),&server.sin_addr);

        if(connect(sock,(struct sockaddr*)&server,sizeof server))
            return true;
        return false;
    }
};